package com.bfsoft.springbootinit.util;

import com.googlecode.aviator.AviatorEvaluator;
import com.googlecode.aviator.Expression;
import org.flowable.bpmn.model.Process;
import org.flowable.bpmn.model.*;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.repository.ProcessDefinition;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;

/**
 * @author Tony
 * @date 2021/4/19 20:51
 */
public class FindNextNodeUtil {

	/**
	 * 获取下一步骤的用户任务
	 *
	 * @param repositoryService
	 * @param map
	 * @return
	 */
	public static List < UserTask > getNextUserTasks ( RepositoryService repositoryService ,
                                                       org.flowable.task.api.Task task , Map < String,Object > map ) {
		List < UserTask > data = new ArrayList <> ();
		ProcessDefinition processDefinition =
                repositoryService.createProcessDefinitionQuery ().processDefinitionId ( task.getProcessDefinitionId () ).singleResult ();
		BpmnModel bpmnModel = repositoryService.getBpmnModel ( processDefinition.getId () );
		Process mainProcess = bpmnModel.getMainProcess ();
		Collection < FlowElement > flowElements = mainProcess.getFlowElements ();
		String key = task.getTaskDefinitionKey ();
		FlowElement flowElement = bpmnModel.getFlowElement ( key );
		next ( flowElements , flowElement , map , data );
		return data;
	}

	/**
	 * 启动流程时获取下一步骤的用户任务
	 *
	 * @param repositoryService
	 * @param map
	 * @return
	 */
	public static List < UserTask > getNextUserTasksByStart ( RepositoryService repositoryService ,
                                                              ProcessDefinition processDefinition , Map < String,
            Object > map ) {
		List < UserTask > data = new ArrayList <> ();
		BpmnModel bpmnModel = repositoryService.getBpmnModel ( processDefinition.getId () );
		Process mainProcess = bpmnModel.getMainProcess ();
		Collection < FlowElement > flowElements = mainProcess.getFlowElements ();
		String key = null;
		// 找到开始节点 并获取唯一key
		for ( FlowElement flowElement : flowElements ) {
			if ( flowElement instanceof StartEvent ) {
				key = flowElement.getId ();
				break;
			}
		}
		FlowElement flowElement = bpmnModel.getFlowElement ( key );
		List < SequenceFlow > sequenceFlows = ( ( StartEvent ) flowElement ).getOutgoingFlows ();
		// 获取出口连线, 此时从开始节点往后,只能是一个出口
		if ( ! sequenceFlows.isEmpty () ) {
			SequenceFlow sequenceFlow = sequenceFlows.get ( 0 );
			FlowElement targetFlowElement = sequenceFlow.getTargetFlowElement ();
			next ( flowElements , targetFlowElement , map , data );
		}
		return data;
	}


	/**
	 * 查找下一节点
	 *
	 * @param flowElements
	 * @param flowElement
	 * @param map
	 * @param nextUser
	 */
	public static void next ( Collection < FlowElement > flowElements , FlowElement flowElement , Map < String,
            Object > map , List < UserTask > nextUser ) {
		//如果是结束节点
		if ( flowElement instanceof EndEvent ) {
			//如果是子任务的结束节点
			if ( getSubProcess ( flowElements , flowElement ) != null ) {
				flowElement = getSubProcess ( flowElements , flowElement );
			}
		}
		//获取Task的出线信息--可以拥有多个
		List < SequenceFlow > outGoingFlows = null;
		if ( flowElement instanceof Task ) {
			outGoingFlows = ( ( Task ) flowElement ).getOutgoingFlows ();
		}
		else if ( flowElement instanceof Gateway ) {
			outGoingFlows = ( ( Gateway ) flowElement ).getOutgoingFlows ();
		}
		else if ( flowElement instanceof StartEvent ) {
			outGoingFlows = ( ( StartEvent ) flowElement ).getOutgoingFlows ();
		}
		else if ( flowElement instanceof SubProcess ) {
			outGoingFlows = ( ( SubProcess ) flowElement ).getOutgoingFlows ();
		}
		else if ( flowElement instanceof CallActivity ) {
			outGoingFlows = ( ( CallActivity ) flowElement ).getOutgoingFlows ();
		}
		if ( outGoingFlows != null && outGoingFlows.size () > 0 ) {
			//遍历所有的出线--找到可以正确执行的那一条
			for ( SequenceFlow sequenceFlow : outGoingFlows ) {
				//1.有表达式，且为true
				//2.无表达式
				String expression = sequenceFlow.getConditionExpression ();
				if ( expression == null || expressionResult ( map , expression.substring ( expression.lastIndexOf (
                        "{" ) + 1 , expression.lastIndexOf ( "}" ) ) ) ) {
					//出线的下一节点
					String nextFlowElementID = sequenceFlow.getTargetRef ();
					if ( checkSubProcess ( nextFlowElementID , flowElements , nextUser ) ) {
						continue;
					}

					//查询下一节点的信息
					FlowElement nextFlowElement = getFlowElementById ( nextFlowElementID , flowElements );
					//调用流程
					if ( nextFlowElement instanceof CallActivity ) {
						CallActivity ca = ( CallActivity ) nextFlowElement;
						if ( ca.getLoopCharacteristics () != null ) {
							UserTask userTask = new UserTask ();
							userTask.setId ( ca.getId () );

							userTask.setId ( ca.getId () );
							userTask.setLoopCharacteristics ( ca.getLoopCharacteristics () );
							userTask.setName ( ca.getName () );
							nextUser.add ( userTask );
						}
						next ( flowElements , nextFlowElement , map , nextUser );
					}
					//用户任务
					if ( nextFlowElement instanceof UserTask ) {
						nextUser.add ( ( UserTask ) nextFlowElement );
					}
					//排他网关
					else if ( nextFlowElement instanceof ExclusiveGateway ) {
						next ( flowElements , nextFlowElement , map , nextUser );
					}
					//并行网关
					else if ( nextFlowElement instanceof ParallelGateway ) {
						next ( flowElements , nextFlowElement , map , nextUser );
					}
					//接收任务
					else if ( nextFlowElement instanceof ReceiveTask ) {
						next ( flowElements , nextFlowElement , map , nextUser );
					}
					//服务任务
					else if ( nextFlowElement instanceof ServiceTask ) {
						next ( flowElements , nextFlowElement , map , nextUser );
					}
					//子任务的起点
					else if ( nextFlowElement instanceof StartEvent ) {
						next ( flowElements , nextFlowElement , map , nextUser );
					}
					//结束节点
					else if ( nextFlowElement instanceof EndEvent ) {
						next ( flowElements , nextFlowElement , map , nextUser );
					}
				}
			}
		}
	}

	/**
	 * 判断是否是多实例子流程并且需要设置集合类型变量
	 */
	public static boolean checkSubProcess ( String id , Collection < FlowElement > flowElements ,
                                            List < UserTask > nextUser ) {
		for ( FlowElement flowElement1 : flowElements ) {
			if ( flowElement1 instanceof SubProcess && flowElement1.getId ().equals ( id ) ) {

				SubProcess sp = ( SubProcess ) flowElement1;
				if ( sp.getLoopCharacteristics () != null ) {
					//                    String inputDataItem = sp.getLoopCharacteristics().getInputDataItem();
					UserTask userTask = new UserTask ();
					userTask.setId ( sp.getId () );
					userTask.setLoopCharacteristics ( sp.getLoopCharacteristics () );
					userTask.setName ( sp.getName () );
					nextUser.add ( userTask );
					return true;
				}
			}
		}

		return false;

	}

	/**
	 * 查询一个节点的是否子任务中的节点，如果是，返回子任务
	 *
	 * @param flowElements 全流程的节点集合
	 * @param flowElement 当前节点
	 * @return
	 */
	public static FlowElement getSubProcess ( Collection < FlowElement > flowElements , FlowElement flowElement ) {
		for ( FlowElement flowElement1 : flowElements ) {
			if ( flowElement1 instanceof SubProcess ) {
				for ( FlowElement flowElement2 : ( ( SubProcess ) flowElement1 ).getFlowElements () ) {
					if ( flowElement.equals ( flowElement2 ) ) {
						return flowElement1;
					}
				}
			}
		}
		return null;
	}


	/**
	 * 根据ID查询流程节点对象, 如果是子任务，则返回子任务的开始节点
	 *
	 * @param Id 节点ID
	 * @param flowElements 流程节点集合
	 * @return
	 */
	public static FlowElement getFlowElementById ( String Id , Collection < FlowElement > flowElements ) {
		for ( FlowElement flowElement : flowElements ) {
			if ( flowElement.getId ().equals ( Id ) ) {
				//如果是子任务，则查询出子任务的开始节点
				if ( flowElement instanceof SubProcess ) {
					return getStartFlowElement ( ( ( SubProcess ) flowElement ).getFlowElements () );
				}
				return flowElement;
			}
			if ( flowElement instanceof SubProcess ) {
				FlowElement flowElement1 = getFlowElementById ( Id ,
                        ( ( SubProcess ) flowElement ).getFlowElements () );
				if ( flowElement1 != null ) {
					return flowElement1;
				}
			}
		}
		return null;
	}

	/**
	 * 返回流程的开始节点
	 *
	 * @param flowElements 节点集合
	 * @description:
	 */
	public static FlowElement getStartFlowElement ( Collection < FlowElement > flowElements ) {
		for ( FlowElement flowElement : flowElements ) {
			if ( flowElement instanceof StartEvent ) {
				return flowElement;
			}
		}
		return null;
	}

	/**
	 * 校验el表达式
	 *
	 * @param map
	 * @param expression
	 * @return
	 */
	public static boolean expressionResult ( Map < String,Object > map , String expression ) {
		Expression exp = AviatorEvaluator.compile ( expression );
		return ( Boolean ) exp.execute ( map );
	}
}
